﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

using zijian666.AnyExtensions.Privates;

namespace zijian666.AnyExtensions;

/// <summary>
/// 枚举类型的扩展方法
/// </summary>
public static class EnumExtensions
{
    /// <summary>
    /// 获取跟枚举项相关的字段
    /// </summary>
    public static FieldInfo? GetFieldInfo<T>(this T key)
        where T : struct, Enum
    {
        try
        {
            var type = typeof(T);
            var name = Enum.GetName(type, key);
            if (string.IsNullOrEmpty(name))
            {
                return null;
            }
            var field = typeof(T).GetField(name);
            return field;
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// 获取枚举值的描述
    /// </summary>
    /// <typeparam name="T">枚举类型</typeparam>
    /// <param name="value">枚举值</param>
    /// <param name="nullToName">如果描述不存在，是否返回枚举项的名称</param>
    /// <returns></returns>
    public static string? GetDescription<T>(this T value, bool nullToName = false)
        where T : struct, Enum
    {
        var enumType = value.GetType();

        if (enumType.IsDefined(typeof(FlagsAttribute)))
        {
            var desc = value.FlagsSplit()
                        .Select(x => x.GetFieldInfo())
                        .WithoutNull()
                        .Select(x => x.GetCustomAttribute<DescriptionAttribute>()?.Description ?? (nullToName ? x.Name : null))
                        .WithoutWhiteSpace()
                        .ToList();
            return desc.Count == 0 ? null : desc.Join(",");
        }

        var name = Enum.GetName(enumType, value);
        if (name is not null)
        {
            var desc = enumType.GetField(name)?.GetCustomAttribute<DescriptionAttribute>()?.Description;
            if (desc is null)
            {
                return nullToName ? name : null;
            }
            return desc;
        }
        return null;
    }

    /// <summary>
    /// 拆分 <seealso cref="FlagsAttribute"/> 标记的枚举
    /// </summary>
    public static IEnumerable<T> FlagsSplit<T>(this T @enum)
        where T : struct, Enum
    {
        var type = @enum.GetType();
        if (type is null || !type.IsEnum || !type.IsDefined(typeof(FlagsAttribute)))
        {
            throw new NotSupportedException("仅支持 [Flag] 标记的枚举");
        }
        var array = (IList)Enum.GetValues(type);
        for (var i = 0; i < array.Count; i++)
        {
            var symbol = (T)array[i]!;
            if (default(T).Equals(symbol))
            {
                continue;
            }
            if (@enum.HasFlag(symbol))
            {
                yield return symbol;
            }
        }
    }

    /// <summary>
    /// 合并枚举
    /// </summary>
    public static T? FlagsCombine<T>(this IEnumerable<T> enums)
        where T : Enum
    {
        using var ee = enums.GetEnumerator();

        if (!ee.MoveNext())
        {
            return default;
        }

        var type = ee.Current.GetType();

        if (!type.IsEnum || !type.IsDefined(typeof(FlagsAttribute)))
        {
            throw new NotSupportedException("仅支持 [Flag] 标记的枚举");
        }

        var uderlyingType = Enum.GetUnderlyingType(type);

        if (uderlyingType == typeof(int))
        {
            var val = 0;
            do
            {
                val |= Convert.ToInt32(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        else if (uderlyingType == typeof(byte))
        {
            byte val = 0;
            do
            {
                val |= Convert.ToByte(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        else if (uderlyingType == typeof(long))
        {
            long val = 0;
            do
            {
                val |= Convert.ToInt64(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        else if (uderlyingType == typeof(sbyte))
        {
            sbyte val = 0;
            do
            {
                val |= Convert.ToSByte(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        else if (uderlyingType == typeof(short))
        {
            short val = 0;
            do
            {
                val |= Convert.ToInt16(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        else if (uderlyingType == typeof(uint))
        {
            uint val = 0;
            do
            {
                val |= Convert.ToUInt32(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        else if (uderlyingType == typeof(ushort))
        {
            ushort val = 0;
            do
            {
                val |= Convert.ToUInt16(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        else if (uderlyingType == typeof(ulong))
        {
            ulong val = 0;
            do
            {
                val |= Convert.ToUInt64(ee.Current);
            } while (ee.MoveNext());
            return val == 0 ? default : val.ConvertTo<T>();
        }
        return default;
    }
}
